package com.vaadin.incubator.bugrap.model.facade;

import java.util.Collection;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.NoResultException;
import javax.persistence.Persistence;
import javax.persistence.Query;

import org.eclipse.persistence.expressions.ExpressionBuilder;
import org.eclipse.persistence.jpa.JpaEntityManager;
import org.eclipse.persistence.jpa.JpaHelper;
import org.eclipse.persistence.sessions.Session;

public class FacadeImpl implements IFacade {

	protected EntityManagerFactory emf = null;
	protected EntityManager em = null;

	public FacadeImpl() {

	}

	public FacadeImpl(String name, Map<String, Object> options) {
		emf = Persistence.createEntityManagerFactory(name, options);
	}

	public void init(String name, Map<String, Object> options) {
		emf = Persistence.createEntityManagerFactory(name, options);
	}

	public <A extends AbstractEntity> A find(Class<A> clazz, Long id) {
		EntityManager em = getEntityManager();
		try {
			return em.find(clazz, id);
		} finally {
			em.close();
		}
	}

	@SuppressWarnings("unchecked")
	public <A extends AbstractEntity> List<A> list(Class<A> clazz) {
		EntityManager em = getEntityManager();
		try {
			ExpressionBuilder builder = new ExpressionBuilder();
			JpaEntityManager jpaEm = JpaHelper.getEntityManager(em);
			Query query = jpaEm.createQuery(builder, clazz);
			return query.getResultList();
		} finally {
			em.close();
		}
	}

	@SuppressWarnings("unchecked")
	public <A extends AbstractEntity> List<A> list(String queryStr,
			Map<String, Object> parameters) {
		EntityManager em = getEntityManager();
		try {
			Query query = em.createQuery(queryStr);
			if (parameters != null) {
				for (String key : parameters.keySet()) {
					query.setParameter(key, parameters.get(key));
				}
			}

			return query.getResultList();
		} finally {
			em.close();
		}
	}

	@SuppressWarnings("unchecked")
	public <A extends AbstractEntity> List<A> list(String queryStr,
			Map<String, Object> parameters, int max) {
		EntityManager em = getEntityManager();
		try {
			Query query = em.createQuery(queryStr);
			query.setMaxResults(max);
			if (parameters != null) {
				for (String key : parameters.keySet()) {
					query.setParameter(key, parameters.get(key));
				}
			}

			return query.getResultList();
		} finally {
			em.close();
		}
	}

	@SuppressWarnings("unchecked")
	public <A extends AbstractEntity> A find(String queryStr,
			Map<String, Object> parameters) {
		EntityManager em = getEntityManager();
		try {
			Query query = em.createQuery(queryStr);
			if (parameters != null) {
				for (String key : parameters.keySet()) {
					query.setParameter(key, parameters.get(key));
				}
			}

			return (A) query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		} finally {
			em.close();
		}
	}

	public <A extends AbstractEntity> A store(A pojo) {
		EntityManager em = getEntityManager();
		A saved = pojo;
		try {
			em.getTransaction().begin();
			if (pojo.getId() != -1) {
				saved = em.merge(pojo);
			} else {
				em.persist(pojo);
			}
			em.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			em.close();
		}
		return saved;
	}

	public <A extends AbstractEntity> void storeAll(Collection<A> pojos) {
		EntityManager em = getEntityManager();
		try {
			em.getTransaction().begin();

			for (AbstractEntity pojo : pojos) {
				if (pojo.getId() != -1) {
					em.merge(pojo);
				} else {
					em.persist(pojo);
				}
			}
			em.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			em.close();
		}
	}

	public void delete(AbstractEntity pojo) {
		// If it isn't stored, it can't be removed
		if (pojo.getId() == -1) {
			return;
		}

		EntityManager em = getEntityManager();
		try {
			em.getTransaction().begin();
			Object entity = em.find(pojo.getClass(), pojo.getId());
			em.remove(entity);
			em.getTransaction().commit();
		} finally {
			em.close();
		}
	}

	public <A extends AbstractEntity> void deleteAll(Collection<A> pojos) {
		if (pojos == null) {
			return;
		}

		EntityManager em = getEntityManager();
		try {
			em.getTransaction().begin();
			for (A pojo : pojos) {
				// If it isn't stored, it can't be removed
				if (pojo.getId() == -1) {
					continue;
				}
				Object entity = em.find(pojo.getClass(), pojo.getId());
				if (entity != null) {
					em.remove(entity);
				}
			}
			em.getTransaction().commit();
		} finally {
			em.close();
		}
	}

	protected EntityManager getEntityManager() {
		if ((em == null || !em.isOpen()) && emf != null) {
			em = emf.createEntityManager();
		}

		return em;
	}

	protected Session getSession() {
		JpaEntityManager emImpl = (JpaEntityManager) getEntityManager()
				.getDelegate();
		return emImpl.getSession();
	}

	public void close() {
		if (em != null && em.isOpen()) {
			em.clear();
			em.close();
		}
	}

	public void kill() {
		if (em != null && em.isOpen()) {
			em.close();
		}
		if (emf != null) {
			emf.close();
		}
	}

	public long count(String queryStr, Map<String, Object> parameters) {
		EntityManager em = getEntityManager();
		try {
			Query query = em.createQuery(queryStr);
			if (parameters != null) {
				for (String key : parameters.keySet()) {
					query.setParameter(key, parameters.get(key));
				}
			}

			return (Long) query.getSingleResult();
		} catch (NoResultException e) {
			return 0;
		} finally {
			em.close();
		}
	}

}
